using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;

public class SfxProfiler : ISfxProfiler
{
    /// <summary>
    /// 每个Group的X方向线程数
    /// </summary>
    private const int THREAD_GROUP_X = 32;
    /// <summary>
    /// 每个Group的Y方向线程数
    /// </summary>
    private const int THREAD_GROUP_Y = 32;
    /// <summary>
    /// 最大Group数量的开方值，也就是一边的长度。
    /// 128 * 32 = 4096，也就是说RT不要超过4096都没问题。
    /// </summary>
    private const int MAX_GROUP_COUNT_SQRT = 128;
    /// <summary>
    /// 最大Group数量。多分配一些，支持计算4K的RT，避免越界
    /// </summary>
    private const int MAX_GROUP_COUNT = MAX_GROUP_COUNT_SQRT * MAX_GROUP_COUNT_SQRT;
    /// <summary>
    /// 循环特效给一个超大的持续时间
    /// </summary>
    private const float LOOP_DURATION = 100000.0f;

    private const string OVERDRAW_OPAQUE_SHADER_PATH = "{0}/OverdrawCyclesOpaque.shader";

    private const string OVERDRAW_TRANSPARENT_SHADER_PATH = "{0}/OverdrawCyclesTransparent.shader";

    private const string COMPLEXITY_COMPUTE_SHADER_PATH = "{0}/OverdrawAnalysis.compute";

    private const string MATERIAL_COMPLEXITY_CACH_PATH = "{0}/MaterialComplexityCache.asset";
    
    private const string PERF_STANDARD_PATH = "{0}/SfxPerformanceStandard.asset";

    internal const string REPORTER_REL_PATH = "Tools/FXPerfReporter";

    private Action m_OnEndProfile;
    
    private SfxProfilerRunner mSfxProfilerRunner;
    private int m_ProfileInstCount;
    private GameObject m_CurrentEffectInst;

    private bool m_IsPlaying;

    private readonly Vector3[] m_InputData = new Vector3[MAX_GROUP_COUNT];
    private readonly Vector3[] m_ResultData = new Vector3[MAX_GROUP_COUNT];
    
    private Camera m_TestCamera;
    private RenderTexture m_TestRT;
    private Camera m_VisualizedCamera;
    private Camera m_DrawCamera;
    private RenderTexture m_VisualizedPerformanceRT;
    private Mesh m_VisualizedMesh;
    private MeshRenderer m_VisualizeRenderer;
    private ComputeShader m_ComputeShader;
    private ComputeBuffer m_Result;
    private bool m_Profiling;

    private Material m_OverdrawOpaque;
    private Material m_OverdrawTransparent;
    private MaterialComplexityCache m_MaterialComplexityCache;
    private SfxPerformanceStandard m_PerfStandard;

    private MethodInfo m_GameViewRepaintMethod;
    private MethodInfo m_GameViewFocus;
    private object m_GameView;

    private FXPerformanceData m_PerformanceData;
    private const float SAMPLE_INTERVAL = 0.03f;
    private float m_SampleTime;
    
    private MethodInfo m_CalculateEffectUIDataMethod;

    private class OverdrawMaterialReplacement
    {
        public Renderer m_Renderer;
        public Material[] m_OrigMaterials;
        public Material[] m_OverdrawMaterials;

        public int m_ReplaceCount;
        public bool m_Loading;
    }

    private readonly List<OverdrawMaterialReplacement> m_OverdrawMaterialReplacements = new List<OverdrawMaterialReplacement>();
    private static readonly int s_CyclesPropertyID = Shader.PropertyToID("_Cycles");
    private static readonly int s_OverdrawRTPropertyID = Shader.PropertyToID("OverdrawRT");
    private static readonly int s_VisualizedPerformanceRTPropertyID = Shader.PropertyToID("VisualizedPerformanceRT");
    private static readonly int s_ResultPropertyID = Shader.PropertyToID("Result");
    private static readonly int s_BufferSizeXPropertyID = Shader.PropertyToID("BufferSizeX");

    private string CompileDestPath
    {
        get
        {
            string projectPath = Application.dataPath.Replace("Assets", "");
            string destPath = projectPath + "/TempShaderCompiled";
            return destPath;
        }
    }

    private static string editorResourcesPath = string.Empty;

    public static string EditorResourcesPath
    {
        get
        {
            if (string.IsNullOrEmpty(editorResourcesPath))
            {
                string[] standardGUIDs = AssetDatabase.FindAssets("t:SfxPerformanceStandard");
                if (standardGUIDs.Length == 0)
                {
                    Debug.LogError("Fail to find EditorResources path of SfxProfiler!");
                    return string.Empty;
                }
                string standardPath = AssetDatabase.GUIDToAssetPath(standardGUIDs[0]);
                editorResourcesPath = Path.GetDirectoryName(standardPath)!.Replace("\\", "/");
            }
            return editorResourcesPath;
        }
    }

    public void SetEndProfileCallbakc(Action callback)
    {
        m_OnEndProfile = callback;
    }
    
    // 该函数通过反射调用
    // ReSharper disable once UnusedMember.Local
    private static void OnEnterPlayMode()
    {
        GameObject profilerObj = GameObject.Find("ProfilerObj");
        SfxProfiler inst = new SfxProfiler
        {
            mSfxProfilerRunner = profilerObj.GetComponent<SfxProfilerRunner>()
        };
        inst.mSfxProfilerRunner.m_Profiler = inst;
        inst.m_ProfileInstCount = inst.mSfxProfilerRunner.m_ProfileInputs.Count;
        
        inst.mSfxProfilerRunner.m_EffectInsts.Clear();
        inst.mSfxProfilerRunner.m_EffectDurations.Clear();
        
        for (int i = 0; i < inst.mSfxProfilerRunner.m_ProfileInputs.Count; ++i)
        {
            SfxProfilerRunner.ProfileInput input = inst.mSfxProfilerRunner.m_ProfileInputs[i];
            GameObject prefab = input.m_Prefab;
            GameObject effectInst = UnityEngine.Object.Instantiate(prefab);
            effectInst.SetActive(false);
            
            float effectDuration = 0.0f;
            ParticleSystem[] particleSystems = effectInst.GetComponentsInChildren<ParticleSystem>();
            foreach (ParticleSystem ps in particleSystems)
            {
                float duration = LOOP_DURATION;
                if (!ps.main.loop)
                {
                    duration = ps.main.duration;
                }
                effectDuration = Mathf.Max(effectDuration, duration);
            }
            Animation[] animations = effectInst.GetComponentsInChildren<Animation>();
            foreach (Animation a in animations)
            {
                float duration = LOOP_DURATION;
                if (a.clip == null || !a.playAutomatically)
                {
                    continue;
                }
                if (!a.clip.isLooping)
                {
                    duration = a.clip.length;
                }
                effectDuration = Mathf.Max(effectDuration, duration);
            }

            if (effectDuration == 0.0f || Math.Abs(effectDuration - LOOP_DURATION) < Mathf.Epsilon)
            {
                effectDuration = 5.0f;
            }
            
            inst.mSfxProfilerRunner.m_EffectInsts.Add(effectInst);
            inst.mSfxProfilerRunner.m_EffectDurations.Add(effectDuration);
        }
    }
    
    public void OnStartProfile(GameObject effectInst, float duration, int index)
    {
        if (effectInst == null)
        {
            return;
        }
        m_CurrentEffectInst = effectInst;
        
        m_Profiling = true;
        
        m_PerformanceData = new FXPerformanceData
        {
            m_FXName = m_CurrentEffectInst.name.Replace("(Clone)", "")
        };
        m_SampleTime = 0.0f;
        m_PerformanceData.m_Duration = duration;
#if UNITY_2017_1_OR_NEWER
        m_CalculateEffectUIDataMethod = typeof(ParticleSystem).GetMethod("CalculateEffectUIData", BindingFlags.Instance | BindingFlags.NonPublic);
#else
        m_CalculateEffectUIDataMethod = typeof(ParticleSystem).GetMethod("CountSubEmitterParticles", BindingFlags.Instance | BindingFlags.NonPublic);
#endif
        
        EditorUtility.DisplayProgressBar("提示", "开始测试性能...", 0.0f);

        if (index == 0)
        {
            CreateProfileEnv();
        }
        RepalceMaterials();
        ReportMemory();
        ReportParticleSystem();
        mSfxProfilerRunner.m_UpdateToStartProfile = UpdateToStartProfile;
    }

    private void UpdateToStartProfile(GameObject effectInst, string gpuAchitecture)
    {
        m_CurrentEffectInst = effectInst;
        int repalcementIndex = 0;
        for (; repalcementIndex < m_OverdrawMaterialReplacements.Count; ++repalcementIndex)
        {
            OverdrawMaterialReplacement replacement = m_OverdrawMaterialReplacements[repalcementIndex];
            if (replacement.m_ReplaceCount < replacement.m_OrigMaterials.Length)
            {
                if (!replacement.m_Loading)
                {
                    replacement.m_Loading = true;

                    Material origMat = replacement.m_OrigMaterials[replacement.m_ReplaceCount];
                    Material replaceMat = replacement.m_OverdrawMaterials[replacement.m_ReplaceCount];
                    if (origMat == null || replaceMat == null)
                    {
                        ++replacement.m_ReplaceCount;
                        replacement.m_Loading = false;
                        break;
                    }
                    
                    EditorUtility.DisplayProgressBar("提示", $"开始分析材质({origMat.name})复杂度...",
                        (float)repalcementIndex / m_OverdrawMaterialReplacements.Count);

                    string gpu = gpuAchitecture;
                    m_PerformanceData.m_GPUArchitecture = gpuAchitecture;

                    if (m_MaterialComplexityCache != null)
                    {
                        if (m_MaterialComplexityCache.TryGetMaterialComplexityInfo(origMat, gpu, out MaterialComplexityInfo complexityInfo))
                        {
                            m_PerformanceData.m_MaterialCycles.Add(new MaterialShaderCycles()
                            {
                                m_MaterialName = origMat.name,
                                m_Cycles = complexityInfo.m_Complexity,
                                m_Bound = complexityInfo.m_Bound
                            });
                            replaceMat.SetFloat(s_CyclesPropertyID, complexityInfo.m_Complexity);
                            ++replacement.m_ReplaceCount;
                            replacement.m_Loading = false;
                            continue;
                        }
                    }
                    
                    ShaderComplexityUtils.CalculateMaterialComplexity(
                        origMat, gpu, CompileDestPath,
                        (shaderInfos, _, success) =>
                        {
                            if (success && shaderInfos != null && shaderInfos.Count == 0)
                            {
                                EditorUtility.DisplayDialog("错误", 
                                    $"材质{origMat.name}无法解析到正确变体，导致无法测试复杂度。可能是因为材质里有冗余数据，请先清除冗余数据！",
                                    "确定");
                            }
                            if (m_OverdrawMaterialReplacements.Count == 0)
                            {
                                return;
                            }
                            for (int s = 0; success && shaderInfos != null && s < shaderInfos.Count; ++s)
                            {
                                ShaderInfo shaderInfo = shaderInfos[s];
                                if (shaderInfo.m_ShaderType == ShaderComplexityUtils.FRAGMENT_SHADER_TYPE)
                                {
                                    ShaderBaseInfo.ShaderCycles cycles = shaderInfo.m_DefaultInfo.m_TotalShaderCycles;
                                    float maxCycles = cycles.m_A;
                                    string bound = "Arithmetic";
                                    if (maxCycles < cycles.m_T)
                                    {
                                        maxCycles = cycles.m_T;
                                        bound = "Texture";
                                    }
                                    if (maxCycles < cycles.m_V)
                                    {
                                        maxCycles = cycles.m_V;
                                        bound = "Varying";
                                    }
                                    if (maxCycles < cycles.m_LS)
                                    {
                                        maxCycles = cycles.m_LS;
                                        bound = "Load/Store";
                                    }
                                    replaceMat.SetFloat(s_CyclesPropertyID, maxCycles);
                                    
                                    m_PerformanceData.m_MaterialCycles.Add(new MaterialShaderCycles()
                                    {
                                        m_MaterialName = origMat.name,
                                        m_Cycles = maxCycles,
                                        m_Bound = bound
                                    });
                                    if (m_MaterialComplexityCache != null)
                                    {
                                        m_MaterialComplexityCache.AddComplexityInfo(origMat, gpu, maxCycles, bound);
                                        EditorUtility.SetDirty(m_MaterialComplexityCache);
                                        AssetDatabase.SaveAssetIfDirty(m_MaterialComplexityCache);
                                    }
                                    // LogUtil.LogError("The max shader cycles of " + origMat.name + " is " + maxCycles);
                                    break;
                                }
                            }
                            
                            ++replacement.m_ReplaceCount;
                            replacement.m_Loading = false;
                        });
                }
                break;
            }
        }

        if (repalcementIndex >= m_OverdrawMaterialReplacements.Count)
        {
            mSfxProfilerRunner.m_UpdateToStartProfile = null;
            mSfxProfilerRunner.m_OnUpdateProfile = UpdateProfiler;
            m_CurrentEffectInst.SetActive(true);
            
            EditorUtility.ClearProgressBar();
        }
    }

    public void ForceEndProfile()
    {
        OnEndProfile(m_ProfileInstCount);
    }
    
    public void OnEndProfile(int index)
    {
        mSfxProfilerRunner.m_UpdateToStartProfile = null;
        mSfxProfilerRunner.m_OnUpdateProfile = null;
        
        m_Profiling = false;
        ResumeMaterials();
        
        m_OverdrawMaterialReplacements.Clear();
        
        EditorUtility.ClearProgressBar();
        
        m_PerformanceData = null;
        
        if (index + 1 >= m_ProfileInstCount)
        {
            if (m_OnEndProfile != null)
            {
                m_OnEndProfile();
            }
            
            DirectoryInfo destDir = new DirectoryInfo(CompileDestPath);
            if (destDir.Exists)
            {
                destDir.Delete(true);
            }
        
            EditorApplication.isPlaying = false;
            
            DestroyProfileEnv();
        }
    }

    private void GeneratePerfWarnings(string gpuArchitecture)
    {
        if (m_PerfStandard == null)
        {
            return;
        }

        string gpu = gpuArchitecture;
        
        PerfStandardWithGPU standard = null;
        foreach (PerfStandardWithGPU perfStandard in m_PerfStandard.m_PerfStandards)
        {
            if (gpu == perfStandard.m_GPUArchitecture)
            {
                standard = perfStandard;
                break;
            }
        }
        if (standard == null)
        {
            return;
        }
        
        foreach (MaterialShaderCycles matShaderCycles in m_PerformanceData.m_MaterialCycles)
        {
            if (matShaderCycles.m_Cycles > standard.m_MaxShaderComplexity)
            {
                m_PerformanceData.m_PerfWarnings.Add($"材质【{matShaderCycles.m_MaterialName}】最大复杂度为 {matShaderCycles.m_Cycles} Cycles/Pixel，" +
                                                     $"建议 <= {standard.m_MaxShaderComplexity}");
            }
        }

        if (m_PerformanceData.m_ObjNativeSize > standard.m_MaxPrefabSize)
        {
            m_PerformanceData.m_PerfWarnings.Add($"Prefab的内存占用为 {EditorUtility.FormatBytes(m_PerformanceData.m_ObjNativeSize)}，" +
                                                 $"建议 <= {EditorUtility.FormatBytes(standard.m_MaxPrefabSize)}");
        }

        if (m_PerformanceData.m_TextureCount > standard.m_MaxTextureCount)
        {
            m_PerformanceData.m_PerfWarnings.Add($"贴图数量为 {m_PerformanceData.m_TextureCount} 张，" +
                                                 $"建议 <= {standard.m_MaxTextureCount} 张");
        }

        if (m_PerformanceData.m_TextureSize > standard.m_MaxTextureSize)
        {
            m_PerformanceData.m_PerfWarnings.Add($"贴图的内存占用为 {EditorUtility.FormatBytes(m_PerformanceData.m_TextureSize)}，" +
                                                 $"建议 <= {EditorUtility.FormatBytes(standard.m_MaxTextureSize)}");
        }

        float maxAvgCycles = 0.0f;
        float maxAvgOverdraw = 0.0f;
        for (int i = 0; i < m_PerformanceData.m_TotalShaderCycles.Count; ++i)
        {
            FXPerformanceFrameData shaderCycles = m_PerformanceData.m_TotalShaderCycles[i];
            FXPerformanceFrameData overdraw = m_PerformanceData.m_TotalOverdraw[i];
            FXPerformanceFrameData pixels = m_PerformanceData.m_Pixels[i];

            maxAvgCycles = Mathf.Max(maxAvgCycles, pixels.m_Value == 0.0f ? 0.0f : (float)Math.Round(shaderCycles.m_Value / pixels.m_Value, 2));
            maxAvgOverdraw = Mathf.Max(maxAvgOverdraw, pixels.m_Value == 0.0f ? 0.0f : (float)Math.Round(overdraw.m_Value / pixels.m_Value, 2));
        }
        if (maxAvgCycles > standard.m_MaxAvgShaderComplexityToEffect)
        {
            m_PerformanceData.m_PerfWarnings.Add($"运行中最大平均Shader复杂度为 {maxAvgCycles} Cycles/Pixel，" +
                                                 $"建议 <= {standard.m_MaxAvgShaderComplexityToEffect} Cycles/Pixel");
        }
        if (maxAvgOverdraw > standard.m_MaxAvgOverdrawToEffect)
        {
            m_PerformanceData.m_PerfWarnings.Add($"运行中最大平均Overdraw为 {maxAvgOverdraw} 层/Pixel，" +
                                                 $"建议 <= {standard.m_MaxAvgOverdrawToEffect} 层/Pixel");
        }

        float maxBatches = 0.0f;
        float maxRendererCount = 0.0f;
        float maxPSCount = 0.0f;
        float maxParticleCount = 0.0f;
        for (int i = 0; i < m_PerformanceData.m_BatchCounts.Count; ++i)
        {
            FXPerformanceFrameData batches = m_PerformanceData.m_BatchCounts[i];
            FXPerformanceFrameData rendererCount = m_PerformanceData.m_RendererCounts[i];
            FXPerformanceFrameData psCount = m_PerformanceData.m_ParticleSystemCounts[i];
            FXPerformanceFrameData particleCount = m_PerformanceData.m_ParticleCounts[i];

            maxBatches = Mathf.Max(maxBatches, batches.m_Value);
            maxRendererCount = Mathf.Max(maxRendererCount, rendererCount.m_Value);
            maxPSCount = Mathf.Max(maxPSCount, psCount.m_Value);
            maxParticleCount = Mathf.Max(maxParticleCount, particleCount.m_Value);
        }
        if (maxBatches > standard.m_MaxBatches)
        {
            m_PerformanceData.m_PerfWarnings.Add($"运行中最大渲染批次为 {maxBatches}，" +
                                                 $"建议 <= {standard.m_MaxBatches}");
        }
        if (maxRendererCount > standard.m_MaxRendererCount)
        {
            m_PerformanceData.m_PerfWarnings.Add($"运行中最大渲染器数量为 {maxRendererCount}，" +
                                                 $"建议 <= {standard.m_MaxRendererCount}");
        }
        if (maxPSCount > standard.m_MaxParticleSystemCount)
        {
            m_PerformanceData.m_PerfWarnings.Add($"运行中最大粒子发射器数量为 {maxPSCount}，" +
                                                 $"建议 <= {standard.m_MaxParticleSystemCount}");
        }
        if (maxParticleCount > standard.m_MaxParticleCount)
        {
            m_PerformanceData.m_PerfWarnings.Add($"运行中最大粒子数为 {maxParticleCount}，" +
                                                 $"建议 <= {standard.m_MaxParticleCount}");
        }
    }

    private void CreateProfileEnv()
    {
        if (m_TestCamera == null)
        {
            GameObject cameraGameObj = new GameObject("ProfileCamera");
            //cameraGameObj.hideFlags = HideFlags.DontSave | HideFlags.HideInHierarchy;
            
            Transform cameraTrans = cameraGameObj.transform;
            cameraTrans.position = new Vector3(4.173032f, 12.05137f, -2.711998f);
            cameraTrans.eulerAngles = new Vector3(55.0f, 0.0f, 0.0f);
            cameraTrans.localScale = Vector3.one;

            m_TestCamera = cameraGameObj.AddComponent<Camera>();
            m_TestCamera.clearFlags = CameraClearFlags.Color;
            m_TestCamera.backgroundColor = Color.clear;
            m_TestCamera.fieldOfView = 30.0f;
            m_TestCamera.nearClipPlane = 1.0f;
            m_TestCamera.farClipPlane = 150.0f;
            m_TestCamera.depth = -2.0f;
            m_TestCamera.useOcclusionCulling = false;
            m_TestCamera.cullingMask = ~(1 << 2);
            m_TestCamera.enabled = false;

            GameObject drawCamObj = UnityEngine.Object.Instantiate(cameraGameObj);
            drawCamObj.name = "DrawCamera";
            m_DrawCamera = drawCamObj.GetComponent<Camera>();
            m_DrawCamera.backgroundColor = Color.gray;
            m_DrawCamera.enabled = true;

            GameObject visualCamObj = new GameObject("VisualizedCamera");
            //visualCamObj.hideFlags = HideFlags.HideAndDontSave;
            Transform visualCameraTrans = visualCamObj.transform;
            visualCameraTrans.position = Vector3.zero;
            visualCameraTrans.rotation = Quaternion.identity;
            visualCameraTrans.localScale = Vector3.one;
            
            m_VisualizedCamera = visualCamObj.AddComponent<Camera>();
            m_VisualizedCamera.clearFlags = CameraClearFlags.Depth;
            m_VisualizedCamera.backgroundColor = Color.gray;
            m_VisualizedCamera.orthographic = true;
            m_VisualizedCamera.orthographicSize = 5.0f;
            m_VisualizedCamera.nearClipPlane = 0.1f;
            m_VisualizedCamera.farClipPlane = 150.0f;
            m_VisualizedCamera.depth = -1.0f;
            m_VisualizedCamera.useOcclusionCulling = false;
            m_VisualizedCamera.cullingMask = 1 << 2;
            m_VisualizedCamera.enabled = true;
            
            if (!SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.RGFloat))
            {
                EditorUtility.DisplayDialog("错误", "当前设备不支持RGFloat格式的RenderTexture，请换台设备使用本工具！", "确定");
                return;
            }
            m_TestRT = new RenderTexture(m_TestCamera.pixelWidth, m_TestCamera.pixelHeight, 0, RenderTextureFormat.RGFloat)
            {
                name = "TestRT"
            };
            m_TestCamera.targetTexture = m_TestRT;

            m_PerformanceData.m_CameraWidth = m_TestCamera.pixelWidth;
            m_PerformanceData.m_CameraHeight = m_TestCamera.pixelHeight;
            
            m_VisualizedPerformanceRT = new RenderTexture(m_TestCamera.pixelWidth, m_TestCamera.pixelHeight, 0, RenderTextureFormat.ARGB32)
            {
                name = "VisualizedPerformanceRT",
                enableRandomWrite = true
            };

            m_VisualizedMesh = new Mesh
            {
                name = "VisualizedMesh"
            };
            float camWidth = m_VisualizedCamera.aspect * 5.0f;

            float scale = 0.1f;
            scale *= scale;
            
            Vector3[] meshPositions = {
                new Vector3(-camWidth, -5.0f, 1.0f),
                new Vector3(-camWidth,  5.0f * scale, 1.0f),
                new Vector3( camWidth * scale,  5.0f * scale, 1.0f),
                new Vector3( camWidth * scale, -5.0f, 1.0f),
            };
            Vector2[] meshUVs = {
                new Vector2(0.0f, 0.0f),
                new Vector2(0.0f, 1.0f),
                new Vector2(1.0f, 1.0f),
                new Vector2(1.0f, 0.0f)
            };
            int[] indices = { 0, 1, 2, 2, 3, 0 };
            m_VisualizedMesh.vertices = meshPositions;
            m_VisualizedMesh.uv = meshUVs;
            m_VisualizedMesh.triangles = indices;

            GameObject visualizedROBj = new GameObject("VisualizedRendererObj")
            {
                //visualizedROBj.hideFlags = HideFlags.HideAndDontSave;
                layer = 2
            };
            Transform vroTrans = visualizedROBj.transform;
            vroTrans.position = Vector3.zero;
            vroTrans.rotation = Quaternion.identity;
            vroTrans.localScale = Vector3.one;
            m_VisualizeRenderer = visualizedROBj.AddComponent<MeshRenderer>();
            MeshFilter meshFilter = visualizedROBj.AddComponent<MeshFilter>();
            meshFilter.mesh = m_VisualizedMesh;
            Material tempMaterial = new Material(Shader.Find("SfxProfile/Transparent"))
            {
                mainTexture = m_VisualizedPerformanceRT
            };
            m_VisualizeRenderer.sharedMaterial = tempMaterial;

            m_ComputeShader = AssetDatabase.LoadAssetAtPath<ComputeShader>(string.Format(COMPLEXITY_COMPUTE_SHADER_PATH, EditorResourcesPath));
            for (int i = 0; i < MAX_GROUP_COUNT; ++i)
            {
                // 全部初始化为0，保证ComputeShader里面Result的默认值是0
                m_InputData[i] = Vector3.zero;
            }
            m_Result = new ComputeBuffer(m_TestCamera.pixelWidth * m_TestCamera.pixelHeight, sizeof(float) * 3);
        }
        
        Vector3 objPos = new Vector3(4.399929f, 0.05000812f, 4.756035f);
        m_CurrentEffectInst.transform.position = objPos;
        
        Tools.current = Tool.Move;
        Tools.viewTool = ViewTool.None;
        foreach (SceneView view in SceneView.sceneViews)
        {
            view.Focus();
            view.LookAt(objPos);
        }

        if (m_GameView == null)
        {
            Assembly assembly = typeof(EditorWindow).Assembly;
            Type type = assembly.GetType("UnityEditor.GameView");
            m_GameViewRepaintMethod = type.GetMethod("Repaint", BindingFlags.Instance | BindingFlags.Public);
            m_GameViewFocus = type.GetMethod("Focus", BindingFlags.Instance | BindingFlags.Public);
            m_GameView = EditorWindow.GetWindow(type);
        }

        if (m_MaterialComplexityCache == null)
        {
            m_MaterialComplexityCache = AssetDatabase.LoadAssetAtPath<MaterialComplexityCache>(
                string.Format(MATERIAL_COMPLEXITY_CACH_PATH, EditorResourcesPath));
        }

        if (m_PerfStandard == null)
        {
            m_PerfStandard = AssetDatabase.LoadAssetAtPath<SfxPerformanceStandard>(
                string.Format(PERF_STANDARD_PATH, EditorResourcesPath));
        }
    }

    private void DestroyProfileEnv()
    {
        if (m_TestCamera != null)
        {
            UnityEngine.Object.DestroyImmediate(m_TestCamera.gameObject);
            m_TestCamera = null;
        }
        if (m_VisualizedCamera != null)
        {
            UnityEngine.Object.DestroyImmediate(m_VisualizedCamera.gameObject);
            m_VisualizedCamera = null;
        }
        if (m_DrawCamera != null)
        {
            UnityEngine.Object.DestroyImmediate(m_DrawCamera.gameObject);
            m_DrawCamera = null;
        }

        if (m_TestRT != null)
        {
            m_TestRT.Release();
            m_TestRT = null;
        }
        if (m_VisualizedPerformanceRT != null)
        {
            m_VisualizedPerformanceRT.Release();
            m_VisualizedPerformanceRT = null;
        }
        
        if (m_VisualizedMesh != null)
        {
            UnityEngine.Object.DestroyImmediate(m_VisualizedMesh);
            m_VisualizedMesh = null;
        }
        if (m_VisualizeRenderer != null)
        {
            UnityEngine.Object.DestroyImmediate(m_VisualizeRenderer.gameObject);
            m_VisualizeRenderer = null;
        }
    }

    private void ResumeMaterials()
    {
        foreach (OverdrawMaterialReplacement replacement in m_OverdrawMaterialReplacements)
        {
            replacement.m_Renderer.sharedMaterials = replacement.m_OrigMaterials;
        }
        m_OverdrawMaterialReplacements.Clear();
    }

    private void RepalceMaterials()
    {
        if (m_OverdrawOpaque == null)
        {
            Shader shader = AssetDatabase.LoadAssetAtPath<Shader>(string.Format(OVERDRAW_OPAQUE_SHADER_PATH, EditorResourcesPath));
            m_OverdrawOpaque = new Material(shader)
            {
                name = "OverdrawOpaque"
            };
        }
        if (m_OverdrawTransparent == null)
        {
            Shader shader = AssetDatabase.LoadAssetAtPath<Shader>(string.Format(OVERDRAW_TRANSPARENT_SHADER_PATH, EditorResourcesPath));
            m_OverdrawTransparent = new Material(shader)
            {
                name = "OverdrawTransparent"
            };
        }
        
        m_OverdrawMaterialReplacements.Clear();
        
        List<Material> tmpOverdrawMatList = new List<Material>();
        Renderer[] rs = m_CurrentEffectInst.GetComponentsInChildren<Renderer>(true);
        foreach (Renderer r in rs)
        {
            if (r == null)
            {
                continue;
            }
            foreach (Material mat in r.sharedMaterials)
            {
                if (mat == null)
                {
                    tmpOverdrawMatList.Add(null);
                    continue;
                }
                if (mat.renderQueue >= 0 && mat.renderQueue <= 2500)
                {
                    tmpOverdrawMatList.Add(m_OverdrawOpaque);
                }
                else
                {
                    tmpOverdrawMatList.Add(m_OverdrawTransparent);
                }
            }
            OverdrawMaterialReplacement replacement = new OverdrawMaterialReplacement()
            {
                m_OrigMaterials = r.sharedMaterials,
                m_Renderer = r,
                m_OverdrawMaterials = tmpOverdrawMatList.ToArray(),
                m_ReplaceCount = 0,
                m_Loading = false
            };
            tmpOverdrawMatList.Clear();

            r.sharedMaterials = replacement.m_OverdrawMaterials;
            
            m_OverdrawMaterialReplacements.Add(replacement);
        }
    }

    private void ReportParticleSystem()
    {
        ParticleSystem[] particleSystems = m_CurrentEffectInst.GetComponentsInChildren<ParticleSystem>();
        foreach (ParticleSystem ps in particleSystems)
        {
            List<string> reasons = CheckCulling(ps);
            if (reasons.Count == 0)
            {
                continue;
            }
            string relPath = GetRelPath(m_CurrentEffectInst.transform, ps.transform);
            ReasonCullingFail rcf = new ReasonCullingFail()
            {
                m_RelPath = relPath,
                m_Reasons = reasons
            };
            m_PerformanceData.m_ReasonCullingFails.Add(rcf);
        }
    }
    
    private static List<string> CheckCulling(ParticleSystem particleSystem)
    {
        List<string> reasons = new List<string>();
        if (particleSystem.collision.enabled)
        {
            reasons.Add("勾选了 Collision");
        }

        if (particleSystem.emission.enabled)
        {
            if (particleSystem.emission.rateOverDistance.curveMultiplier != 0)
            {
                reasons.Add("Emission使用了Current（非线性运算）");
            }
        }

        if (particleSystem.externalForces.enabled)
        {
            reasons.Add("勾选了 External Forces");
        }
        if (particleSystem.noise.enabled)
        {
            reasons.Add("勾选了 Noise");
        } 
        if (particleSystem.rotationBySpeed.enabled)
        {
            reasons.Add("勾选了 Rotation By Speed");
        }
        if (particleSystem.shape.enabled)
        {
            ParticleSystemShapeType shapeType = particleSystem.shape.shapeType;
            switch (shapeType)
            {
                case ParticleSystemShapeType.Cone:
                case ParticleSystemShapeType.ConeVolume:
#if UNITY_2017_1_OR_NEWER
                case ParticleSystemShapeType.Donut:
#endif
                case ParticleSystemShapeType.Circle:
                    if(particleSystem.shape.arcMode != ParticleSystemShapeMultiModeValue.Random)
                    {
                        reasons.Add("Shape的Circle-Arc使用了Random模式");
                    }
                    break;
                case ParticleSystemShapeType.SingleSidedEdge:
                    if (particleSystem.shape.radiusMode != ParticleSystemShapeMultiModeValue.Random)
                    {
                        reasons.Add("Shape的Edge-Radius使用了Random模式");
                    }
                    break;
            }
        } 
        if (particleSystem.subEmitters.enabled)
        {
            reasons.Add("勾选了 SubEmitters");
        } 
        if (particleSystem.trails.enabled)
        {
            reasons.Add("勾选了 Trails");
        } 
        if (particleSystem.trigger.enabled)
        {
            reasons.Add("勾选了 Trigger");
        }
        if (particleSystem.limitVelocityOverLifetime.enabled)
        {
            reasons.Add("勾选了 Limit Velocity Over Lifetime");
        }
        if (particleSystem.main.simulationSpace != ParticleSystemSimulationSpace.Local)
        {
            reasons.Add("\nSimulationSpace 不等于 Local");
        }
        if (particleSystem.main.gravityModifierMultiplier != 0)
        {
            reasons.Add("\nGravityModifier 不等于0");
        }
        return reasons;
    }
    
    private static string GetRelPath(Transform root, Transform me)
    {
        if (me == root)
        {
            return string.Empty;
        }
        
        string path = me.name;
        Transform parent = me;
        while (parent != root)
        {
            parent = parent.parent;
            if (parent != root)
            {
                path = parent.name + "/" + path;
            }
        }

        return path;
    }
    
    private void ReportMemory()
    {
        m_PerformanceData.m_TextureCount = 0;
        m_PerformanceData.m_TextureSize = 0L;
        
        m_PerformanceData.m_ObjNativeSize = UnityEngine.Profiling.Profiler.GetRuntimeMemorySizeLong(m_CurrentEffectInst.gameObject);
        m_PerformanceData.m_ObjNativeSizeTxt = EditorUtility.FormatBytes(m_PerformanceData.m_ObjNativeSize);
        
        Assembly assembly = typeof(AssetDatabase).Assembly;
        Type texUtilType = assembly.GetType("UnityEditor.TextureUtil");
        MethodInfo getMemMethod = texUtilType.GetMethod("GetStorageMemorySizeLong", BindingFlags.Public | BindingFlags.Static);
        
        for (int i = 0; i < m_OverdrawMaterialReplacements.Count; ++i)
        {
            OverdrawMaterialReplacement replacement = m_OverdrawMaterialReplacements[i];
            foreach (Material material in replacement.m_OrigMaterials)
            {
                if (material == null)
                {
                    continue;
                }
                int[] texPropIDs = material.GetTexturePropertyNameIDs();
                foreach (int texPropID in texPropIDs)
                {
                    if (!material.HasProperty(texPropID))
                    {
                        continue;
                    }
                    Texture tex = material.GetTexture(texPropID);
                    if (tex == null)
                    {
                        continue;
                    }
                    ++m_PerformanceData.m_TextureCount;
                    if (getMemMethod != null)
                    {
                        m_PerformanceData.m_TextureSize += (long)getMemMethod.Invoke(null, new object[] {tex});
                    }
                }
            }
        }
        
        m_PerformanceData.m_TextureCountTxt = m_PerformanceData.m_TextureCount + " 张";
        m_PerformanceData.m_TextureSizeTxt = EditorUtility.FormatBytes(m_PerformanceData.m_TextureSize);
    }

    private bool UpdateProfiler(GameObject effectInst, string gpuArchitecture, float playProgress, float duration)
    {
        if (!m_Profiling)
        {
            return false;
        }
        m_CurrentEffectInst = effectInst;

        if (m_TestCamera == null || m_TestRT == null)
        {
            ForceEndProfile();
            return false;
        }
        if (playProgress >= duration)
        {
            GeneratePerfWarnings(gpuArchitecture);
            
            string json = JsonUtility.ToJson(m_PerformanceData, true);
            string projectPath = Application.dataPath.Replace("Assets", "");
            string fileName = Path.Combine(projectPath, REPORTER_REL_PATH, "FXPerformanceDataReporter_Data/Test.json")
                .Replace("\\", "/");
            File.WriteAllText(fileName, json);

            string exePath = Path.Combine(projectPath, REPORTER_REL_PATH, "FXPerformanceDataReporter.exe")
                .Replace("\\", "/");;
            ProcessStartInfo startInfo = new ProcessStartInfo()
            {
                FileName = exePath,
                RedirectStandardOutput = false,
                RedirectStandardError = false,
                RedirectStandardInput = false,
                UseShellExecute = false,
                CreateNoWindow = true
            };
            Process process = new Process();
            process.StartInfo = startInfo;
            process.Start();
            
            return true;
        }
        
        if (m_TestCamera.pixelWidth != m_TestRT.width || m_TestCamera.pixelHeight != m_TestRT.height)
        {
            m_TestRT.Release();
            m_TestRT = new RenderTexture(m_TestCamera.pixelWidth, m_TestCamera.pixelHeight, 0, RenderTextureFormat.RGFloat);
            m_Result.Release();
            m_Result = new ComputeBuffer(m_TestCamera.pixelWidth * m_TestCamera.pixelHeight, 3 * sizeof(float));
            
            Debug.LogError("[NodeControllerProfiler.UpdateProfiler] 测试期间不要更改屏幕尺寸");
        }
        
        m_TestCamera.Render();
        
        if (playProgress >= m_SampleTime)
        {
            m_Result.SetData(m_InputData); // 默认值设为0
            int kernel = m_ComputeShader.FindKernel("CSMain");
            m_ComputeShader.SetTexture(kernel, s_OverdrawRTPropertyID, m_TestRT);
            m_ComputeShader.SetTexture(kernel, s_VisualizedPerformanceRTPropertyID, m_VisualizedPerformanceRT);
            m_ComputeShader.SetInt(s_BufferSizeXPropertyID, MAX_GROUP_COUNT_SQRT);
            m_ComputeShader.SetBuffer(kernel, s_ResultPropertyID, m_Result);
            m_ComputeShader.Dispatch(kernel, m_TestRT.width / THREAD_GROUP_X, m_TestRT.height / THREAD_GROUP_Y, 1);
            m_Result.GetData(m_ResultData);
            
            float totalCycles = 0.0f;
            float totalOverdraw = 0.0f;
            float pixels = 0.0f;
            for (int i = 0; i < m_ResultData.Length; ++i)
            {
                // x是总cycles数量；y是总Overdraw数量；z是覆盖的像素数量
                Vector3 v = m_ResultData[i];
                totalCycles += v.x;
                totalOverdraw += v.y;
                pixels += v.z;
            }
            
            int particleCount = 0;
            Renderer[] renderers = m_CurrentEffectInst.GetComponentsInChildren<Renderer>();
            ParticleSystem[] particleSystems = m_CurrentEffectInst.GetComponentsInChildren<ParticleSystem>();
            foreach (ParticleSystem ps in particleSystems)
            {
                int count = 0;
#if UNITY_2017_1_OR_NEWER
                object[] invokeArgs = { count, 0.0f, Mathf.Infinity };
                m_CalculateEffectUIDataMethod.Invoke(ps, invokeArgs);
                count = (int)invokeArgs[0];
#else
                object[] invokeArgs = { count };
                m_CalculateEffectUIDataMethod.Invoke(ps, invokeArgs);
                count = (int)invokeArgs[0];
                count += ps.particleCount;
#endif
                particleCount += count;
            }
            m_PerformanceData.m_RendererCounts.Add(new FXPerformanceFrameData(playProgress, renderers.Length));
            m_PerformanceData.m_ParticleSystemCounts.Add(new FXPerformanceFrameData(playProgress, particleSystems.Length));
            m_PerformanceData.m_ParticleCounts.Add(new FXPerformanceFrameData(playProgress, particleCount));
            int batches = (UnityStats.batches - 1) / 2; // 有一个Mesh用来渲染示意图的，所以减1；两个相机会渲染两遍，所以除以2
            m_PerformanceData.m_BatchCounts.Add(new FXPerformanceFrameData(playProgress, batches));
            
            m_PerformanceData.m_Pixels.Add(new FXPerformanceFrameData(playProgress, pixels));
            m_PerformanceData.m_TotalShaderCycles.Add(new FXPerformanceFrameData(playProgress, totalCycles));
            m_PerformanceData.m_TotalOverdraw.Add(new FXPerformanceFrameData(playProgress, totalOverdraw));
            m_SampleTime += SAMPLE_INTERVAL;
        }
        
        // if (pixels > 0.0f)
        // {
        //     LogUtil.LogError($"Progress:{playProgress}, 平均Cycles：{totalCycles/pixels}, 平均Overdraw：{totalOverdraw/pixels}，覆盖像素：{pixels}");
        // }
        
        if (m_GameView != null && m_GameViewRepaintMethod != null)
        {
            m_GameViewFocus.Invoke(m_GameView, null);
            m_GameViewRepaintMethod.Invoke(m_GameView, null);
        }
        return false;
    }
}